package ldh.im.fxgui.demo;

import dev.onvoid.webrtc.*;
import dev.onvoid.webrtc.media.MediaDevices;
import dev.onvoid.webrtc.media.MediaStreamTrack;
import dev.onvoid.webrtc.media.audio.AudioOptions;
import dev.onvoid.webrtc.media.audio.AudioTrack;
import dev.onvoid.webrtc.media.audio.AudioTrackSource;
import dev.onvoid.webrtc.media.video.VideoCaptureCapability;
import dev.onvoid.webrtc.media.video.VideoDevice;
import dev.onvoid.webrtc.media.video.VideoDeviceSource;
import dev.onvoid.webrtc.media.video.VideoTrack;
import io.netty.channel.Channel;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.FlowPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
import ldh.im.client.CommonLocalSeqCreator;
import ldh.im.dto.base.OkResp;
import ldh.im.dto.business.PingReq;
import ldh.im.dto.business.user.LoginResp;
import ldh.im.dto.business.webrtc.*;
import ldh.im.dto.business.webrtc.mesh.WebRtcP2pCandidateReq;
import ldh.im.dto.business.webrtc.mesh.WebRtcP2pSdpReq;
import ldh.im.dto.enums.SdpType;
import ldh.im.fxbase.component.VideoView;
import ldh.im.fxbase.rpc.SignalCallBack;
import ldh.im.fxbase.rpc.router.SignalConsumerRouter;
import ldh.im.fxbase.rpc.service.RpcSignalService;
import ldh.im.fxbase.service.ChannelService;
import ldh.im.fxbase.util.FxThreadUtil;
import ldh.im.fxbase.util.ThreadPoolUtil;
import ldh.im.util.ConfigUtil;
import ldh.rpc.util.JsonUtil;
import ldh.rpc.LocalSeqCreator;
import ldh.rpc.TerminalType;
import ldh.rpc.client.RpcClientBootstrap;
import ldh.rpc.dto.PongResp;
import ldh.rpc.listener.ChannelEventType;
import ldh.rpc.listener.ChannelListener;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class WebRtcDemo extends Application implements SignalCallBack {

    private static final Logger LOGGER = LoggerFactory.getLogger(WebRtcDemo.class);

    private RpcClientBootstrap rpcClientBootstrap = null;
    private RpcSignalService rpcSignalService;

    private String userId = null;
    private String roomName = null;
    private Map<String, PeerConnectionData> peerConnectionMap = new ConcurrentHashMap<>();

    private VideoView videoView = new VideoView();
    private FlowPane videoPane;

    private PeerConnectionFactory factory = null;
    private RTCConfiguration config = new RTCConfiguration();

    private VideoDeviceSource videoSource = null;
    private AudioTrack audioTrack;
    private VideoTrack videoTrack;

    @Override
    public void start(Stage stage) throws Exception {
        BorderPane borderPane = new BorderPane();

//        videoView.resize(500, 300);
        videoView.setPrefSize(360, 720);
        videoPane = new FlowPane(10, 10);
        videoPane.getChildren().add(videoView);
        borderPane.setCenter(videoPane);

        HBox hBox = createControlPane();

        borderPane.setBottom(hBox);
        hBox.setPadding(new Insets(10));
        hBox.setStyle("-fx-background-color: green");
        Scene scene = new Scene(borderPane, 500, 600);
        stage.setScene(scene);
        stage.show();

        stage.setOnCloseRequest((e)->{
            closeLocalVideo();
            try {
                Thread.sleep(1000);
            } catch (Exception ee) {}
            LOGGER.info("System.close!!!!!!!!!!!!!!");

            Platform.exit();
            System.exit(-1);
        });

    }

    private void startWebSocket() throws Exception {
        ChannelService channelService = new ChannelService();

        String address = ConfigUtil.getString("signal.address", "192.168.1.6");
        int port = ConfigUtil.getInt("signal.port", 9890);

        ChannelListener channelListener = new ChannelListener() {
            @Override
            public void channelEvent(Channel channel, ChannelEventType channelEventType) {

            }
        };

        rpcClientBootstrap = channelService.createWebRtcClientBootstrap(address, port, channelListener);
        LocalSeqCreator localSeqCreator = new CommonLocalSeqCreator("demo", LocalDateTime.now());
        rpcClientBootstrap.getRpcConfig().setLocalSeqCreator(localSeqCreator);
        SignalConsumerRouter signalConsumerRouter = (SignalConsumerRouter) rpcClientBootstrap.getRpcConfig().getRouter(SignalConsumerRouter.class);
        signalConsumerRouter.addSignalCallBack(this);

        rpcSignalService = rpcClientBootstrap.getRpcConfig().getClientBean(RpcSignalService.class);
        WebRtcLoginReq rtcLoginReq = new WebRtcLoginReq();
        rtcLoginReq.setAuthToken("4A2B709345914A19610C05250E0172C2BE9635D6");
        rtcLoginReq.setTenantCode("0002");
        rtcLoginReq.setTerminalType(TerminalType.pc);

        LoginResp loginResp = rpcSignalService.login(rtcLoginReq);
        this.userId = loginResp.getUserId();
    }

    private void openLocalVideo() {
        if (factory != null) {
            closeLocalVideo();
        }
        openVideo();
    }

    private void openVideo() {
        VideoCaptureCapability captureCapability = null;
        List<VideoDevice> devices = null;
        try{
            factory = new PeerConnectionFactory();
            videoSource = new VideoDeviceSource();
            devices = MediaDevices.getVideoCaptureDevices();
            for (VideoDevice device : devices) {
                videoSource.setVideoCaptureDevice(device);
                List<VideoCaptureCapability> videoCaptureCapabilities = MediaDevices.getVideoCaptureCapabilities(device);
                for (VideoCaptureCapability captureCapability1 : videoCaptureCapabilities) {
                    captureCapability = captureCapability1;
                    videoSource.setVideoCaptureCapability(captureCapability);
                    videoSource.start();
                    videoTrack = factory.createVideoTrack("CAM", videoSource);

                    videoTrack.addSink(videoView::setVideoFrame);

                    AudioOptions audioOptions = new AudioOptions();
                    AudioTrackSource audioSource = factory.createAudioSource(audioOptions);
                    audioTrack = factory.createAudioTrack("audioTrack", audioSource);

                    break;
                }
                if (captureCapability != null) {
                    break;
                }
            }
        } catch (Exception e) {
            LOGGER.error("[WebRtcClient open video failure], error: {}", e);
            throw new RuntimeException("open video capture failure");
        }
        if (devices == null || devices.size() < 1) {
            throw new RuntimeException("没有语音设备");
        }
        if (captureCapability == null) {
            throw new RuntimeException("open video capture failure");
        }
    }

    private void joinRoom(String roomName) {
        WebRtcJoinRoomReq joinRoomReq = new WebRtcJoinRoomReq();
        joinRoomReq.setRoomName(roomName);
        WebRtcRoomInfo webRtcRoomInfo = this.rpcSignalService.roomJoin(joinRoomReq);
        this.roomName = webRtcRoomInfo.getName();
        assert this.roomName != null;
    }

    private void createOffer(String remoteUserId, VideoView videoView) {
        PeerConnectionData peerConnectionData = peerConnectionMap.get(remoteUserId);
        if (peerConnectionData == null) {
            RTCPeerConnection peerConnection = createPeerConnection(remoteUserId, videoView);

            peerConnectionMap.put(remoteUserId, new PeerConnectionData(peerConnection, videoView));

            RTCOfferOptions options = new RTCOfferOptions();
            peerConnection.createOffer(options, new OfferSdpCreateSessionDescriptionObserver(remoteUserId, peerConnection));
        }
    }

    @Override
    public PongResp pingToClient(PingReq pingReq) {
        LOGGER.info("signal client receive ping");
        return new PongResp();
    }

    @Override
    public OkResp receiveJoinRoomReq(WebRtcJoinRoomReq joinRoomReq) {
        LOGGER.info("signal client receive join room, data: {}", JsonUtil.toJson(joinRoomReq));
        VideoView videoView = new VideoView();
        FxThreadUtil.runLater(()-> {
//            videoView.resize(200, 200);
            videoPane.getChildren().add(videoView);
        });

        this.createOffer(joinRoomReq.getUserId(), videoView);
        return new OkResp(joinRoomReq.getLocalSeq());
    }

    @Override
    public OkResp receiveExitRoomReq(WebRtcExitRoomReq exitRoomReq) {
        LOGGER.info("signal client receive exit room, data: {}", JsonUtil.toJson(exitRoomReq));
        String userId = exitRoomReq.getUserId();
        PeerConnectionData peerConnectionData = peerConnectionMap.remove(userId);
        if (peerConnectionData != null) {
            peerConnectionData.getPeerConnection().close();
            FxThreadUtil.runLater(()->{
                videoPane.getChildren().remove(peerConnectionData.getVideoView());
            });
        }
        return new OkResp(exitRoomReq.getLocalSeq());
    }

    @Override
    public OkResp receiveSdp(WebRtcP2pSdpReq sdpReq) {
        LOGGER.info("signal client receive sdp, data: {}", JsonUtil.toJson(sdpReq));
        String remoteUserId = sdpReq.getUserId();
        if (sdpReq.getSdpType() == SdpType.offer) { // 被连接端
            VideoView videoView = new VideoView();
            FxThreadUtil.runLater(()->{
//                videoView.resize(200, 200);
                videoPane.getChildren().add(videoView);
            });
            RTCPeerConnection peerConnection = createPeerConnection(remoteUserId, videoView);
            RTCSessionDescription sessionDescription = new RTCSessionDescription(RTCSdpType.OFFER, sdpReq.getSdp());
            peerConnection.setRemoteDescription(sessionDescription, new RemoteSessionDescriptionObserver(remoteUserId, peerConnection));
        } else if (sdpReq.getSdpType() == SdpType.answer) { // 发起端
            PeerConnectionData peerConnectionData = peerConnectionMap.get(remoteUserId);
            RTCPeerConnection peerConnection = peerConnectionData.getPeerConnection();
            RTCSessionDescription sessionDescription = new RTCSessionDescription(RTCSdpType.ANSWER, sdpReq.getSdp());
            peerConnection.setRemoteDescription(sessionDescription, new SetSessionDescriptionObserver() {

                @Override
                public void onSuccess() {
                    LOGGER.info("setRemoteDescription success");
                }

                @Override
                public void onFailure(String s) {
                    LOGGER.info("setRemoteDescription failure");
                }
            });
        }
        return new OkResp(sdpReq.getLocalSeq());
    }

    @Override
    public OkResp receiveIceCandidateReq(WebRtcP2pCandidateReq webRtcP2pCandidateReq) {
        LOGGER.info("signal client receive candidate, data: {}", JsonUtil.toJson(webRtcP2pCandidateReq));
        String remoteUserId = webRtcP2pCandidateReq.getUserId();
        PeerConnectionData peerConnectionData = peerConnectionMap.get(remoteUserId);
        if (peerConnectionData != null) {
            RTCPeerConnection peerConnection = peerConnectionData.getPeerConnection();
            IceCandidate candidate = webRtcP2pCandidateReq.getIceCandidate();
            peerConnection.addIceCandidate(new RTCIceCandidate(candidate.getSdpMid(), candidate.getSdpMLineIndex(), candidate.getCandidate()));
        }
        return new OkResp(webRtcP2pCandidateReq.getLocalSeq());
    }

    @Override
    public OkResp receiveDelRoomReq(WebRtcDelRoomReq delRoomReq) {
        ThreadPoolUtil.submit(()->{
            closeLocalVideo();
        });
        return new OkResp(delRoomReq.getLocalSeq());
    }

    private void closeLocalVideo() {
        peerConnectionMap.values().forEach((peerConnectionData) -> {
            try {
                peerConnectionData.getPeerConnection().close();
            } catch (Exception e) {}
        });
        if (videoSource != null) {
            LOGGER.info("videoSource stop!!!!!!!!!!!!!!");
            videoSource.stop();
        }

        if (factory != null) {
            factory.dispose();
        }
        LOGGER.info("localVideo close!!!!!!!!!!!!!!");
    }

    private RTCPeerConnection createPeerConnection(String userId, VideoView videoView2) {
        SignalPeerConnectionObserver signalPeerConnectionObserver = new SignalPeerConnectionObserver(userId, videoView2);
        RTCPeerConnection peerConnection = factory.createPeerConnection(config, signalPeerConnectionObserver);

        PeerConnectionData peerConnectionData = new PeerConnectionData(peerConnection, videoView2);
        signalPeerConnectionObserver.setPeerConnectionData(peerConnectionData);
        peerConnectionMap.put(userId, peerConnectionData);

        peerConnection.addTrack(audioTrack, List.of("stream"));
        peerConnection.addTrack(videoTrack, List.of("stream"));

        return peerConnection;
    }

    private class SignalPeerConnectionObserver implements PeerConnectionObserver {

        private String userId;
        private VideoView videoView;
        private PeerConnectionData peerConnectionData;

        public SignalPeerConnectionObserver(String userId, VideoView videoView) {
            this.userId = userId;
            this.videoView = videoView;
        }

        public void setPeerConnectionData(PeerConnectionData peerConnectionData) {
            this.peerConnectionData = peerConnectionData;
        }

        @Override
        public void onIceGatheringChange(RTCIceGatheringState state) {
            LOGGER.info("onIceGatheringChange: " + state.name());
            if(state == RTCIceGatheringState.COMPLETE) {
                peerConnectionData.setEndOfCandidates(true);
            }
        }

        @Override
        public void onIceCandidate(RTCIceCandidate rtcIceCandidate) {
            if (rtcIceCandidate.sdpMLineIndex > 0) return;
            LOGGER.info(String.format("onIceCandidate: sdp: %s, sdpMid: %s, sdpMLineIndex: %s, serverUrl: " +
                            "%s", rtcIceCandidate.sdp, rtcIceCandidate.sdpMid, rtcIceCandidate.sdpMLineIndex,
                    rtcIceCandidate.serverUrl));

            WebRtcP2pCandidateReq candidateReq = new WebRtcP2pCandidateReq();
            IceCandidate candidate = new IceCandidate();
            candidate.setCandidate(rtcIceCandidate.sdp);
            candidate.setSdpMid(rtcIceCandidate.sdpMid);
            candidate.setCandidate(rtcIceCandidate.sdp);
            candidate.setServerUrl(rtcIceCandidate.serverUrl);
            candidateReq.setIceCandidate(candidate);
            candidateReq.setRoomName(roomName);
            candidateReq.setToUserId(userId);
            rpcSignalService.iceCandidate(candidateReq);
        }

        @Override
        public void onTrack(RTCRtpTransceiver transceiver) {
            MediaStreamTrack track = transceiver.getReceiver().getTrack();
            String kind = track.getKind();
            LOGGER.info("receiver: " + kind);

            if (kind.equals(MediaStreamTrack.VIDEO_TRACK_KIND)) {
                VideoTrack videoTrack = (VideoTrack) track;
                videoTrack.addSink(frame -> {
//                    LOGGER.info("receiver 22222222222222222222222222222222");
                    if (videoView != null) {
                        try {
                            frame.retain();
                            videoView.setVideoFrame(frame);
                            frame.release();
                        } catch (Exception e) {
                            e.printStackTrace();
                            LOGGER.error("Render error : " + e.getMessage());
                        }
                    }
                });
            }
        }
    }

    class PeerConnectionData {
        private boolean endOfCandidates;
        private VideoView videoView;
        private RTCPeerConnection peerConnection;

        public PeerConnectionData(RTCPeerConnection peerConnection, VideoView videoView) {
            this.peerConnection = peerConnection;
            this.videoView = videoView;
        }

        public boolean isEndOfCandidates() {
            return endOfCandidates;
        }

        public void setEndOfCandidates(boolean endOfCandidates) {
            this.endOfCandidates = endOfCandidates;
        }

        public RTCPeerConnection getPeerConnection() {
            return peerConnection;
        }

        public void setPeerConnection(RTCPeerConnection peerConnection) {
            this.peerConnection = peerConnection;
        }

        public VideoView getVideoView() {
            return videoView;
        }
    }

    class OfferSdpCreateSessionDescriptionObserver implements CreateSessionDescriptionObserver {

        private String remoteUserId;
        private RTCPeerConnection rtcPeerConnection;

        public OfferSdpCreateSessionDescriptionObserver(String remoteUserId, RTCPeerConnection rtcPeerConnection) {
            this.remoteUserId = remoteUserId;
            this.rtcPeerConnection = rtcPeerConnection;
        }

        @Override
        public void onSuccess(RTCSessionDescription rtcSessionDescription) {
            String offerSdp = rtcSessionDescription.sdp;
            LOGGER.info("offerSdp: " + offerSdp);

            rtcPeerConnection.setLocalDescription(rtcSessionDescription, new SetSessionDescriptionObserver() {

                @Override
                public void onSuccess() {
                    LOGGER.info("offer setLocalDescription success!!!!!!!!!!!!!");

                    WebRtcP2pSdpReq sdpReq = new WebRtcP2pSdpReq();
                    sdpReq.setRoomName(roomName);
                    sdpReq.setToUserId(remoteUserId);
                    sdpReq.setSdp(rtcSessionDescription.sdp);
                    sdpReq.setSdpType(SdpType.offer);
                    rpcSignalService.sdp(sdpReq);
                }

                @Override
                public void onFailure(String error) {
                    LOGGER.error("offer setLocalDescription failure, error: " + error);

                }
            });
        }

        @Override
        public void onFailure(String error) {
            LOGGER.error("error: " + error);
        }
    }

    class AnswerSdpCreateSessionDescriptionObserver implements CreateSessionDescriptionObserver {

        private String remoteUserId;
        private RTCPeerConnection rtcPeerConnection;

        public AnswerSdpCreateSessionDescriptionObserver(String remoteUserId, RTCPeerConnection rtcPeerConnection) {
            this.remoteUserId = remoteUserId;
            this.rtcPeerConnection = rtcPeerConnection;
        }

        @Override
        public void onSuccess(RTCSessionDescription rtcSessionDescription) {
            String answerSdp = rtcSessionDescription.sdp;
            LOGGER.info("answerSdp: " + answerSdp);

            rtcPeerConnection.setLocalDescription(rtcSessionDescription, new SetSessionDescriptionObserver() {

                @Override
                public void onSuccess() {
                    LOGGER.info("answer setLocalDescription success!!!!!!!!!!!!!");
                }

                @Override
                public void onFailure(String error) {
                    LOGGER.error("answer setLocalDescription failure, error: " + error);
                }
            });

            WebRtcP2pSdpReq sdpReq = new WebRtcP2pSdpReq();
            sdpReq.setRoomName(roomName);
            sdpReq.setToUserId(remoteUserId);
            sdpReq.setSdp(rtcSessionDescription.sdp);
            sdpReq.setSdpType(SdpType.answer);
            rpcSignalService.sdp(sdpReq);
        }

        @Override
        public void onFailure(String error) {
            LOGGER.error("error: " + error);
        }
    }

    class RemoteSessionDescriptionObserver implements SetSessionDescriptionObserver {

        private String remoteUserId;
        private RTCPeerConnection rtcPeerConnection;

        public RemoteSessionDescriptionObserver(String remoteUserId, RTCPeerConnection rtcPeerConnection) {
            this.remoteUserId = remoteUserId;
            this.rtcPeerConnection = rtcPeerConnection;
        }

        @Override
        public void onSuccess() {
            rtcPeerConnection.createAnswer(new RTCAnswerOptions(), new AnswerSdpCreateSessionDescriptionObserver(remoteUserId, rtcPeerConnection));
        }

        @Override
        public void onFailure(String error) {
            LOGGER.error("error: " + error);
        }
    }

    private HBox createControlPane(){
        TextField textField = new TextField();

        Button start = new Button("start");
        start.setOnAction((e)->{
            try {
                openLocalVideo();
                startWebSocket();
            } catch (Exception error) {
                error.printStackTrace();
            }
        });

        Button connectBtn = new Button("connect");
        connectBtn.setOnAction((e)-> {
            String roomName = textField.getText().trim();
            if (roomName.equals("")) return;
            joinRoom(roomName);
        });

        Button closeBtn = new Button("close");
        closeBtn.setOnAction((e)->{
            if (peerConnectionMap.size() < 1) return;
            for (Map.Entry<String, PeerConnectionData> data : peerConnectionMap.entrySet()) {
                String uid = data.getKey();

                PeerConnectionData peerConnectionData = data.getValue();
                RTCPeerConnection peerConnection = peerConnectionData.getPeerConnection();

                ThreadPoolUtil.submit(()->{
                    try {
                        WebRtcExitRoomReq exitRoomReq = new WebRtcExitRoomReq();
                        exitRoomReq.setRoomName(roomName);
                        rpcSignalService.roomExit(exitRoomReq);

                        FxThreadUtil.runLater(()->{
                            videoPane.getChildren().remove(peerConnectionData.getVideoView());
                        });
                        peerConnection.close();
                    } catch (Exception error) {
                        error.printStackTrace();
                    }
                });
            }
        });

        Button closeAudioBtn = new Button("关闭音频");
        closeAudioBtn.setUserData("close");
        closeAudioBtn.setOnAction((e)->{
            if (closeAudioBtn.getUserData().toString().equals("close")) {
                peerConnectionMap.forEach((key, value)->{
                    RTCRtpSender[] senders = value.getPeerConnection().getSenders();
                    for (RTCRtpSender sender : senders) {
                        if (sender.getTrack().getKind().equals("audio")) {
                            sender.getTrack().setEnabled(false);
                            System.out.println(key + "移除音频");
                        }
                    }
                });
                closeAudioBtn.setText("打开音频");
                closeAudioBtn.setUserData("add");
            } else {
                closeAudioBtn.setText("关闭音频");
                peerConnectionMap.forEach((key, value)->{
                    RTCRtpSender[] senders = value.getPeerConnection().getSenders();
                    for (RTCRtpSender sender : senders) {
                        if (sender.getTrack().getKind().equals("audio")) {
                            sender.getTrack().setEnabled(true);
                            System.out.println(key + "开启音频");
                        }
                    }
                });
                closeAudioBtn.setUserData("close");
            }
        });
        Button closeVideoBtn = new Button("关闭视频");
        closeVideoBtn.setUserData("close");
        closeVideoBtn.setOnAction((e)->{
            if (closeVideoBtn.getUserData().toString().equals("close")) {
                peerConnectionMap.forEach((key, value)->{
                    RTCRtpSender[] senders = value.getPeerConnection().getSenders();
                    for (RTCRtpSender sender : senders) {
                        if (sender.getTrack().getKind().equals("video")) {
                            sender.getTrack().setEnabled(false);
                            System.out.println(key + "移除视频");
                        }
                    }
                });
                closeVideoBtn.setText("打开视频");
                closeVideoBtn.setUserData("add");
            } else {
                peerConnectionMap.forEach((key, value)->{
                    RTCRtpSender[] senders = value.getPeerConnection().getSenders();
                    for (RTCRtpSender sender : senders) {
                        if (sender.getTrack().getKind().equals("video")) {
                            sender.getTrack().setEnabled(true);
                            System.out.println(key + "开启视频");
                        }
                    }
                });

                closeVideoBtn.setText("关闭视频");
                closeVideoBtn.setUserData("close");
            }
        });
        HBox hBox = new HBox(10);
        hBox.getChildren().addAll(start, textField, connectBtn, closeBtn, closeAudioBtn, closeVideoBtn);
        return hBox;
    }

    private VideoCaptureCapability getVideoCaptureCapability(VideoDevice device) {
        try {
            List<VideoCaptureCapability> videoCaptureCapabilities = MediaDevices.getVideoCaptureCapabilities(device);
            if (videoCaptureCapabilities.size() > 0) {
                VideoCaptureCapability captureCapability = videoCaptureCapabilities.get(0);
                return captureCapability;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private VideoCaptureCapability getVideoCaptureCapabilityForMany(VideoDevice device) {
        VideoCaptureCapability captureCapability = getVideoCaptureCapability(device);
        int count = 5;
        int index = 0;
        while(captureCapability == null && index < count) {
            index ++;
            try {
                Thread.sleep(1000 * index);
                captureCapability = getVideoCaptureCapability(device);
                if (captureCapability != null) {
                    return captureCapability;
                }
            } catch (Exception e) {

            }
        }
        if (captureCapability == null) {
            captureCapability = new VideoCaptureCapability(500, 300, 30);
        }
        return captureCapability;
    }
}
